use crate::port::portd;
use crate::port::porte;
pub use avr_hal_generic::usart::*;

pub type Usart0<CLOCK, IMODE> = Usart<
    crate::pac::USART0,
    porte::PE0<crate::port::mode::Input<IMODE>>,
    porte::PE1<crate::port::mode::Output>,
    CLOCK,
>;
pub type Usart1<CLOCK, IMODE> = Usart<
    crate::pac::USART1,
    portd::PD2<crate::port::mode::Input<IMODE>>,
    portd::PD3<crate::port::mode::Output>,
    CLOCK,
>;

#[macro_use]
macro_rules! impl_usart_traditional_ex {
    (
        peripheral: $USART:ty,
        register_suffix: $n:expr,
        rx: $rxmod:ident::$RX:ident,
        tx: $txmod:ident::$TX:ident,
    ) => {
        $crate::paste::paste! {
            impl $crate::usart::UsartOps<
                $rxmod::$RX<$crate::port::mode::Input<$crate::port::mode::Floating>>,
                $txmod::$TX<$crate::port::mode::Output>,
            > for $USART {
                fn raw_init<CLOCK>(&mut self, baudrate: $crate::usart::Baudrate<CLOCK>) {
                    self.[<ubrr $n h>].write(|w| unsafe { w.bits((baudrate.ubrr >> 8) as u8) });
                    self.[<ubrr $n l>].write(|w| unsafe { w.bits(baudrate.ubrr as u8) });
                    self.[<ucsr $n a>].write(|w| w.[<u2x $n>]().bit(baudrate.u2x));

                    // Enable receiver and transmitter but leave interrupts disabled.
                    self.[<ucsr $n b>].write(|w| w
                        .[<txen $n>]().set_bit()
                        .[<rxen $n>]().set_bit()
                    );

                    // Set frame format to 8n1 for now.  At some point, this should be made
                    // configurable, similar to what is done in other HALs.
                    self.[<ucsr $n c>].write(|w| w
                        .[<umsel $n>]().usart_async()
                        .[<ucsz $n>]().chr8()
                        .[<usbs $n>]().stop1()
                        .[<upm $n>]().disabled()
                    );
                }

                fn raw_deinit(&mut self) {
                    // Wait for any ongoing transfer to finish.
                    $crate::nb::block!(self.raw_flush()).ok();
                    self.[<ucsr $n b>].reset();
                }

                fn raw_flush(&mut self) -> $crate::nb::Result<(), $crate::void::Void> {
                    if self.[<ucsr $n a>].read().[<udre $n>]().bit_is_clear() {
                        Err($crate::nb::Error::WouldBlock)
                    } else {
                        Ok(())
                    }
                }

                fn raw_write(&mut self, byte: u8) -> $crate::nb::Result<(), $crate::void::Void> {
                    // Call flush to make sure the data-register is empty
                    self.raw_flush()?;

                    self.[<udr $n>].write(|w| unsafe { w.bits(byte) });
                    Ok(())
                }

                fn raw_read(&mut self) -> $crate::nb::Result<u8, $crate::void::Void> {
                    if self.[<ucsr $n a>].read().[<rxc $n>]().bit_is_clear() {
                        return Err($crate::nb::Error::WouldBlock);
                    }

                    Ok(self.[<udr $n>].read().bits())
                }

                fn raw_interrupt(&mut self, event: $crate::usart::Event, state: bool) {
                    match event {
                        $crate::usart::Event::RxComplete =>
                            self.[<ucsr $n b>].modify(|_, w| w.[<rxcie $n>]().bit(state)),
                        $crate::usart::Event::DataRegisterEmpty =>
                            self.[<ucsr $n b>].modify(|_, w| w.[<txcie $n>]().bit(state)),
                    }
                }
            }
        }
    };
}

impl_usart_traditional_ex! {
    peripheral: crate::pac::USART0,
    register_suffix: 0,
    rx: porte::PE0,
    tx: porte::PE1,
}
impl_usart_traditional_ex! {
    peripheral: crate::pac::USART1,
    register_suffix: 1,
    rx: portd::PD2,
    tx: portd::PD3,
}
